"""

.. module:: httpclienttask
.. moduleauthor:: @author: 

The purpose of this module is ... 
   
.. seealso:: modules :mod:`<module>`

*Constants*:
.. data:: constant_1
        
    constant_1 is used for ......
        
.. data:: constant_2
        
    constant_2 is used for ......
"""

# Imports
#
from urllib2 import URLError, HTTPError
import httplib
import urllib2
import base64
import logging

from runtime.task import Task
from datastore.resource import Resource

# Constants
#

# Classes
#

class HTTPClientTask(Task):
    """This class performs HTTP client function on another web site (i.e. GET a remote URL).

    *Usage examples*::

        example code

    *Common Arguments for Class methods*:
        <arguments>
    
    *Audience*:
        <audience>
    
    *Constants*:
        .. data:: constant_1
        
            constant_1 is used for ......
        
        .. data:: constant_2
        
            constant_2 is used for ......
    """
    
    # Class constants
    #
    
    def __init__(self, ds, spaces, task_uri, task_def_rs):
        # <purpose>
        #
        # Parameters:
        #     ds : datastore.database.DataStore
        #     spaces : runtime.spaces.Spaces - Reference to spaces
        #     task_uri : String - the uri for this task. The root uri will most
        #                likely be the 'binding' uri for this task.
        #     task_def_rs : datastore.resource.Resource - Task definition resource.
        #
        
        Task.__init__(self, ds, spaces, task_uri, task_def_rs)
        
        self.logger = logging.getLogger('yarra.services.httpclienttask.HTTPClientTask')
        self.logger.log(logging.DEBUG, "ds (%s), spaces (%s), task_uri (%s), task_def_rs (%s)" % (ds, spaces, task_uri, task_def_rs.pretty_print()))
    
    def cb_start(self, method, uri, res, result, context, **kwargs):
        """This function ... 
        
        :param param_1: <class> or <type>
        :param param_2: <class> or <type>
        :rtype: <return type>
        """
        
        uri_httpclient = context.uri('httpclient:')
        context.process(uri_httpclient.request.collection(True), put='cb_create_request')
        context.watch_all(['put'], uri_httpclient.request.collection(True), 'cb_issue_request')
        
    def cb_create_request(self, method, uri, res, result, context, **kwargs):
        """This function create a request. The resource that is 'PUT' to Spaces 
        needs to include:
        - url -- String: url of the endpoint (e.g. http://www.google.com or 
        file:///C:/Blah/yada.exe).
        - http_method -- String: For HTTP request. Valid values are: 'GET', 
        'POST', 'PUT' or 'DELETE'. None for non HTTP Requests.
        - data -- Dictionary: Data for the request.
        
        :param method: <class> or <type>
        :param uri: <class> or <type>
        :param res: <class> or <type>
        :param result: <class> or <type>
        :param context: <class> or <type>
        :param kwargs: <class> or <type>
        :rtype: <return type>
        """
        
        uri_httpclient = context.uri('httpclient:')
        new_uri = uri_httpclient.request.add(self.spaces_context.uuid())
        new_uri = self.spaces_context.put(new_uri, res)
        return (True, new_uri)
    
    def cb_issue_request(self, method, uri, new_uri, result, context, **kwargs):
        """This function ... 
        
        :param method: <class> or <type>
        :param uri: <class> or <type>
        :param new_uri: <class> or <type>
        :param result: <class> or <type>
        :param context: <class> or <type>
        :param kwargs: <class> or <type>
        :rtype: <return type>
        """
        
        if new_uri.split()[-1] == 'response':
            # ignore responses
            return
        resource = Resource()
        res = context.get(new_uri)
        url = res.url
        http_method = res.http_method
        data = res.data
        opener = urllib2.build_opener()
        request = urllib2.Request(url, data=data)
        if http_method:
            request.get_method = lambda: http_method
        try:
            handle = opener.open(request)
            response_content = handle.read()
            # always encode the response
            encoded_content = base64.b64encode(response_content)
            resource.content = encoded_content                  
            resource.code = handle.code       
        except URLError:
            # FIXME: should we just return a 404?
            resource.code = httplib.NOT_FOUND
        except HTTPError, e:
            resource.code = e.code
        self.spaces_context.put(uri.response, resource)